/* Copyright 2002 -- Ira R. Forman and Nate Forman */ 
/**
 * This transformation produces singleton version of the input class.
 * The constructors of the base class are replicated and made private.
 * Static methods name getInstance are created (one per constructor)
 * that are used to obtain instances (if no instance exist, one is constructed).
 * The singleton is stored with a weak reference so that to not impede
 * garbage collection of the singleton.
 *
 * There are two problematic points with this transformation.<ul>
 * <li> The notion of having a parametized getInstance that returns 
 * an existant singleton is problematic because the getInstance method
 * is ignoring its parameters.  Should a getInstance with parameters
 * throw an exception if called when there is an extant singleton?
 * <li> The transformation stores the singleton with at weak reference.
 * Is it necessary to have another form that holds the singleton to
 * prevent garbage collection?
 * </ul>
 */
//start extract C2SingletonC
package c2c;
import java.lang.reflect.*;
import mopex.*;
import java.io.Serializable;

public class C2SingletonC extends C2CTransformation {

    private int numberOfConstructors = 0;

    static public void main( String[] args ) {                             //#1
	new C2SingletonC().createClass( args );
    }

    protected void checkAndProcessArgs( Args args ) {                      //#2
	super.checkAndProcessArgs( args );
	setFinal();
	if ( inputClassObject.isInterface() )
	    throw new C2CException("cannot generate Singleton for interface");
	if ( Serializable.class.isAssignableFrom(inputClassObject) )
	    throw new C2CException("cannot handle Serializable input classes");
	if ( Cloneable.class.isAssignableFrom(inputClassObject) )
	    throw new C2CException("Cloneable and Singleton are conflicting");
    }

    protected UQueue generateImports() {                                   //#3
	return super.generateImports().add("java.lang.ref.*");
    }

    protected String generateClassNamePrefix() {                           //#4
	return "Singleton" + super.generateClassNamePrefix();
    }

    protected String getSuperclass() {return inputClassName;}              //#5

    protected String generateFields() {
	return super.generateFields() 
	    + "static private WeakReference singleton = null;\n";          //#6
    }

    protected final String generateConstructors() {                        //#7
	String result = "";
	Constructor[] cArray = inputClassObject.getDeclaredConstructors();
	String code = "    if (singleton!=null && singleton.get()!=null)\n" +
	              "        throw new RuntimeException(" 
                               + "\"Singleton constructor failure\");\n" +
	              "    singleton = new WeakReference( this );\n";
	if ( cArray.length != 0 ) {
	    for (int i = 0; i < cArray.length; i++ ) {
		result += "private " 
		    + Mopex.createRenamedConstructor( cArray[i], 
						      outputClassName, 
						      code);
	    }
	    numberOfConstructors = cArray.length; 
	} else {
	    result = "private " + outputClassName + "() {" + code + "}\n";
	    numberOfConstructors = 1; 
	}
	return super.generateConstructors() + result;
    }

    protected String generateMethods() {                                 //#8
	String result = "";
	Constructor[] cArray = inputClassObject.getDeclaredConstructors();
	if ( cArray.length != 0 ) {
	    for (int i = 0; i < cArray.length; i++ ) {
		Class[] pta = cArray[i].getParameterTypes();
		String fpl = Mopex.formalParametersToString( pta );
		String apl = Mopex.actualParametersToString( pta );
		Class[] eTypes =  cArray[i].getExceptionTypes();
		int modifiers = cArray[i].getModifiers();
		result += "static " + Modifier.toString( modifiers ) + " " 
		    + outputClassName + " getInstance(" + fpl + ")\n";
		if ( eTypes.length != 0 ) {
		    result 
			+= "    throws " + Mopex.classArrayToString( eTypes ) 
			+ "\n";
		}
		result += "{\n"
		    + "    if (singleton==null || singleton.get()==null)\n"
		    + "        new " + outputClassName + "(" + apl + ");\n"
		    + "    return (" +outputClassName+ ")singleton.get();\n"
		    + "}\n";
	    }
	} else {
	    result = "    static " + outputClassName + " getInstance() {\n"
		+ "        if (singleton==null || singleton.get()==null)\n"
		+ "            singleton = new " + outputClassName + "();\n"
		+ "        return (" + outputClassName + ")singleton.get();\n"
		+ "    }\n";
	}
	return super.generateMethods() + result;
    }

    protected void checkPostconditions() {                               //#9
	super.checkPostconditions();
	if ( outputClassObject.getDeclaredConstructors().length 
	     != numberOfConstructors )
	    throw new C2CException( "non-Singleton constructors added" );
    }
}
//stop extract C2SingletonC
